package edu.pw.p2pclient.networking;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

import android.util.Log;
import edu.pw.p2pclient.data.RemoteDeviceInfo;
import edu.pw.p2pclient.logic.AvailableManager;
import edu.pw.p2pclient.networking.messages.Message;

class TCPListenerThread extends Thread {

	private static final String TAG = "TCPListenerThread";
	private boolean mServerRunning = false;
	private MsgBuffer mBuffer;
	private ArrayList<ConnectionHandler> mHandlers = new ArrayList<ConnectionHandler>();
	private ServerSocket mServerSock;
	private CommunicationService mCommService;

	public TCPListenerThread(MsgBuffer buffer) {

		super();
		mBuffer = buffer;
		mCommService = CommunicationService.getInstance();
	}

	@Override
	public void run() {

		Thread.currentThread().setName(TAG);
		Log.i(TAG, "Starting TCPListenerThread");
		mServerRunning = true;
		try {
			mServerSock = new ServerSocket(CommunicationService.PORT);
			while (mServerRunning) {
				Socket clientSock = mServerSock.accept();
				clientSock.setKeepAlive(true);
				Log.i(TAG, "New TCP connection");
				startNewHandler(clientSock);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			Log.e(TAG, "IOException occured.");
			e.printStackTrace();
		} finally {
			mServerRunning = false;
			try {
				mServerSock.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Log.e(TAG, "Finishing TCPListenerThread.");
			interruptHandlers();
			mCommService.getHandler().sendMessage(
					android.os.Message.obtain(mCommService.getHandler(),
							CommunicationService.TCPLISTENER_ENDED));
		}
	}

	public void startNewHandler(RemoteDeviceInfo info) {

		ConnectionHandler handler = new ConnectionHandler(info, mBuffer);
		synchronized (mHandlers) {
			mHandlers.add(handler);
		}
		handler.start();
	}

	public void startNewHandler(Socket socket) {

		ConnectionHandler handler = new ConnectionHandler(new RemoteDeviceInfo(
				socket.getInetAddress(), socket), mBuffer);
		synchronized (mHandlers) {
			mHandlers.add(handler);
		}
		handler.start();
	}

	private void interruptHandlers() {

		Log.i(TAG, "Interrputing handlers.");
		synchronized (mHandlers) {
			Iterator<ConnectionHandler> it = mHandlers.iterator();
			while (it.hasNext())
				it.next().interrupt();
			it = mHandlers.iterator();
			while (it.hasNext()) {
				try {
					it.next().join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		Log.i(TAG, "Handlers interrputed.");
	}

	@Override
	public void interrupt() {

		Log.i(TAG, "TCPListenerThread interrputed.");
		mServerRunning = false;
		try {
			if (mServerSock != null)
				mServerSock.close();
		} catch (IOException e) {
		}
		interruptHandlers();
		super.interrupt();
	}

	private class ConnectionHandler extends Thread {

		private final static String TAG = "(TCP)ConnectionHandler";
		private RemoteDeviceInfo mInfo;
		private MsgBuffer mBuffer;
		private boolean mRunning = false;
		private boolean mInterrupted = false;

		public ConnectionHandler(RemoteDeviceInfo info, MsgBuffer buffer) {

			mInfo = info;
			if (!mCommService.doIknowThisDevice(mInfo.getAddress())) {
				mCommService.addKnownDevice(mInfo);
			}
			mBuffer = buffer;
		}

		@Override
		public void run() {

			Thread.currentThread().setName(mInfo.getAddress().toString());
			Log.i(TAG, "Starting (TCP)ConnectionHandler.");
			mRunning = true;
			while (mRunning) {
				try {
					Message msg = (Message) mInfo.getObjectInputStream()
							.readObject();
					Log.i("TAG", "Something received!");
					msg.setSenderAddress(mInfo.getAddress());
					mBuffer.put(msg);
				} catch (IOException e) {
					Log.i(TAG,
							"Socket corrupted - ending handler. "
									+ e.getMessage());
					mRunning = false;
				} catch (ClassCastException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			mInfo.close();

			Log.i(TAG, "Removing myself from handlers list.");
			if (!mInterrupted) {
				synchronized (mHandlers) {
					mHandlers.remove(this);
				}
			}
			CommunicationService.getInstance().removeKnownDevice(mInfo);
			AvailableManager.getInstance().deviceRemoved(mInfo);
		}

		@Override
		public void interrupt() {

			Log.i(TAG, "Interrupting (TCP)ConnectionHandler.");
			mRunning = false;
			mInterrupted = true;
			try {
				Socket socket = mInfo.getSocket();
				if (socket != null) {
					mInfo.setSocket(null);
					socket.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			super.interrupt();
		}
	}

}